<?php

namespace app\logic\es\fp;

use app\common\enum\fp\PostAuditEnum;
use app\common\enum\fp\VipUnlockEnum;
use app\common\model\fp\Post;
use app\common\model\fp\PostAudit;
use app\common\model\fp\PostMedia;
use app\common\model\User;
use app\logic\es\EsBase;
use Elastic\Elasticsearch\Exception\ClientResponseException;
use Elastic\Elasticsearch\Exception\MissingParameterException;
use Elastic\Elasticsearch\Exception\ServerResponseException;
use Elastic\Elasticsearch\Response\Elasticsearch;
use Http\Promise\Promise;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use think\facade\Log;
use Throwable;

/**
 * 动态es操作模型
 */
class EsPost extends EsBase
{
    /**
     * 当前操作索引
     * @var string
     */
    protected string $index = "post";

    /**
     * 导入数据
     * @param $params
     * @return Elasticsearch|Promise
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws Throwable
     */
    public function import($params): Elasticsearch|Promise
    {
        try {
            $data = Post::initModel($params['user_id'])->where('id', $params['post_id'])->find();
            if ($data) {
                $data = $data->toArray();
                $data['user'] = User::initModel()->where('id', $params['user_id'])->field("id, nickname, motto")->find()->toArray();
                $media = PostMedia::initModel($params['user_id'])->where('post_id', $params['post_id'])->field('media_type,video_cover_url, media_url')->select()->toArray();
                $media_url = array_column($media, 'media_url');
                $data['media'] = [
                    'media_type' => $media[0]['media_type'],
                    'video_cover_url' => $media[0]['video_cover_url'],
                    'media_url' => $media_url
                ];
            }
            if (!$data) throw new \Exception('动态不存在');

            return $this->update($params['post_id'], $data);
        } catch (Throwable $e) {
            Log::channel('es')->error($e->getMessage() . PHP_EOL . $e->getTraceAsString());
            throw $e;
        }
    }


    /**
     * 执行一个查询操作
     *
     * 该方法根据提供的参数构建一个查询体，然后执行搜索操作它主要用于处理搜索请求，并返回匹配的结果
     * 如果查询过程中出现异常，它会记录错误信息并重新抛出异常
     *
     * @param array $params 查询参数，包括查询关键词、起始位置和结果大小等
     * @return Elasticsearch|Promise 返回查询结果或者一个Promise对象
     * @throws ClientResponseException
     * @throws ServerResponseException
     */
    public function query(array $params): Elasticsearch|Promise
    {
//        $fields = match ($params['type']){
//            'content'=>['content'],
//            //['content', 'user.nickname', 'user.motto']
//            default=>['user.nickname']
//        };
        $fields = [
            'content',
            'content.ngram',    // 支持前缀匹配
            'content.ik_max_word',   // 支持中文分词
            'content.keyword',        // 精确匹配
            'title'
        ];
        // 构建查询体
        $body = [
            'query' => [
                'bool' => [
                    'must' => [
                        'multi_match' => [
                            'query' => $params['query'], // 要搜索的关键词
                            'fields' => $fields , // 搜索字段
                            'operator' => 'or',  // 使用“或”关系
                            'type' => 'best_fields',  // 优先匹配最相关的字段
                        ]
                    ]
                ]

            ],
            'from' => $params['from'] ?? 0,
            'size' => $params['size'] ?? 10,
            'highlight' => [
                'fields' => [
                    'content' => ['pre_tags' => ['<span style=\'color:#FF0055;\'>'], 'post_tags' => ['</span>']],
                    'title' => ['pre_tags' => ['<span style=\'color:#FF0055;\'>'], 'post_tags' => ['</span>']],
                ]
            ],
            'sort' => [
                ['create_time' => 'desc']
            ]
        ];
        if (!empty($params['filter'])) {
            $body['query']['bool']['filter'] = $params['filter'];
        }
        // 执行搜索
        return parent::search($body);
    }

    /**
     * 获取全部数据
     * @param $params
     * @return Elasticsearch|Promise
     * @throws ClientResponseException
     * @throws ServerResponseException
     */
    public function all($params): Elasticsearch|Promise
    {
        // 构建查询体
        $body = [
            'query' => [
                'bool' => [
                    'must' => [
                        'match_all' => new \stdClass()
                    ]
                ]

            ],
            'from' => $params['from'] ?? 0,
            'size' => $params['size'] ?? 10,
            'sort' => [
                ['create_time' => 'desc']
            ],
        ];
        if (!empty($params['filter'])) {
            // 过滤条件
            /*
            "filter" => [
            'term' => ['type' => 1]
            ]
             */
            $body['query']['bool']['filter'] = $params['filter'];
        }

        // 执行搜索
        return parent::search($body);
    }

    /**
     * 根据用户id查询动态
     * @param $params
     * @return Elasticsearch|Promise
     * @throws ClientResponseException
     * @throws ServerResponseException
     */
    public function findByUserIds($params): Elasticsearch|Promise
    {
        // 构建查询体
        $body = [
            'query' => [
                'terms' => [
                    'user_id' => $params['user_ids']
                ]
            ],
            'from' => $params['from'] ?? 0,
            'size' => $params['size'] ?? 10,
            'sort' => [
                ['create_time' => 'desc']
            ]
        ];

        // 执行搜索
        return parent::search($body);
    }

    /**
     * 根据动态id查询动态
     * @param $params
     * @return Elasticsearch|Promise
     * @throws ClientResponseException
     * @throws ServerResponseException
     */
    public function findByIds($params): Elasticsearch|Promise
    {
        // 构建查询体
        $body = [
            'query' => [
                'terms' => [
                    'id' => $params['ids']
                ]
            ],
            'from' => $params['from'] ?? 0,
            'size' => $params['size'] ?? 10,
            'sort' => [
                ['create_time' => 'desc']
            ]
        ];

        // 执行搜索
        return parent::search($body);
    }


    /**
     * 获取关注的动态
     * @param $params
     * @return Elasticsearch|Promise
     * @throws ClientResponseException
     * @throws ServerResponseException
     */
    public function video($params): Elasticsearch|Promise
    {
        // 构建查询体
        $body = [
            'query' => [
                'bool' => [
                    'must' => [
                        'term' => [
                            'media.media_type' => 'video'
                        ]
                    ]
                ]
            ],
            'from' => $params['from'] ?? 0,
            'size' => $params['size'] ?? 10,
            'sort' => [
                ['create_time' => 'desc']
            ]
        ];
        if (!empty($params['filter'])) {
            $body['query']['bool']['filter'] = $params['filter'];
        }
        // 执行搜索
        return parent::search($body);
    }


    /**
     * 获取vip动态
     * @param $params
     * @return Elasticsearch|Promise
     * @throws ClientResponseException
     * @throws ServerResponseException
     */
    public function vip($params): Elasticsearch|Promise
    {
        // 构建查询体
        $body = [
            'query' => [
                'bool' => [
                    'must' => [
                        'term' => [
                            'is_vip_unlock' => VipUnlockEnum::YES->value
                        ]
                    ]
                ]
            ],
            'from' => $params['from'] ?? 0,
            'size' => $params['size'] ?? 10,
            'sort' => [
                ['create_time' => 'desc']
            ]
        ];
        if (!empty($params['filter'])) {
            $body['query']['bool']['filter'] = $params['filter'];
        }
        // 执行搜索
        return parent::search($body);
    }

    /**
     * 初始化索引配置
     * @return Elasticsearch|Promise
     * @throws ClientResponseException
     * @throws MissingParameterException
     * @throws ServerResponseException
     */
    public function initIndexSetting(): Elasticsearch|Promise
    {
        $mappings = [
            'properties' => [
                'title' => [
                    'type' => 'text',
                    'analyzer' => 'ik_max_word', // 中文分词器
                    'fields' => [
                        'keyword' => [
                            'type' => 'keyword',  // 精确匹配字段
                            'ignore_above' => 256
                        ]
                    ]
                ],
                'content' => [
                    'type' => 'text',
                    'fields' => [
                        'keyword' => [
                            'type' => 'keyword',
                            'ignore_above' => 256
                        ],
                        'ik_max_word' => [
                            'type' => 'text',    // 中文分词字段，使用 IK 分词器
                            'analyzer' => 'ik_max_word'
                        ],
                        'ngram' => [
                            'type' => 'text',    // 支持字母和数字匹配，使用 ngram 分词器
                            'analyzer' => 'ngram_analyzer'
                        ]
                    ]
                ]
            ]
        ];

        $settings = [
            'analysis' => [
                'tokenizer' => [
                    'ngram_tokenizer' => [
                        'type' => 'ngram',
                        'min_gram' => 2,  // 最小分词长度
                        'max_gram' => 3, // 最大分词长度
                        'token_chars' => ['letter', 'digit']  // 允许分词字母和数字
                    ]
                ],
                'analyzer' => [
                    'ngram_analyzer' => [
                        'type' => 'custom',
                        'tokenizer' => 'ngram_tokenizer'
                    ]
                ]
            ]
        ];


        return $this->ensureIndexExists($mappings, $settings);
    }

    public function importAll()
    {
        PostAudit::initTable()->where('status', PostAuditEnum::APPROVED->value)
            ->field('id, post_id, user_id')
            ->chunk(100, function($items) {
                $import = [];
                foreach ($items as $audit) {
                    $data = Post::initModel($audit['user_id'])->where('id', $audit['post_id'])->find();
                    if ($data) {
                        $data = $data->toArray();
                        $data['user'] = User::initModel()->where('id', $audit['user_id'])->field("id, nickname, motto")->find()->toArray();
                        $media = PostMedia::initModel($audit['user_id'])->where('post_id', $audit['post_id'])->field('media_type,video_cover_url, media_url')->select()->toArray();
                        $media_url = array_column($media, 'media_url');
                        $data['media'] = [
                            'media_type' => $media[0]['media_type'],
                            'video_cover_url' => $media[0]['video_cover_url'],
                            'media_url' => $media_url
                        ];
                        $import[] = $data;
                    }
                }

                if (!empty($import)) {
                    $this->batches($import);
                }

            });
    }
}